package org.easy.eao.spring.jpa;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.easy.eao.annotations.Retrieve;
import org.easy.eao.exception.ActionInitializeException;
import org.easy.eao.utils.ActionUtils;
import org.easy.eao.utils.ReflectUtils;

/**
 * 实体寻回存取动作实现类
 * @author Frank
 */
public class RetrieveAction extends JpaActionSupport<Retrieve> {

	protected Class<?> entityType;
	protected Class<?> returnType;
	
	@Override
	public void initialize(Annotation annotation, Method method, Class<?> clz) {
		super.initialize(annotation, method, clz);
		this.returnType = method.getReturnType();
		try {
			initializeEntityType(method, clz);
		} catch (Exception e) {
			throw new ActionInitializeException("无法获取 Entity 类型", e);
		}
	}
	
	/**
	 * 根据返回类型初始化实体类型
	 * @param method 执行方法
	 * @param clz 声明类
	 */
	@SuppressWarnings("rawtypes")
	private void initializeEntityType(Method method, Class<?> clz) {
		if (!ReflectUtils.hasGenericReturnType(method)) {
			// 非泛型声明的处理
			Class<?> returnType = method.getReturnType();
			if (returnType.isArray()) {
				// 数组的话，直接获取元素的类型
				entityType = returnType.getComponentType();
				return;
			}
			entityType = returnType;
		} else {
			// 泛型声明的处理
			Type type = method.getGenericReturnType();
			if (GenericArrayType.class.isAssignableFrom(type.getClass())) {
				// 数组的话，直接获取元素的实际类型
				GenericArrayType arrayType = (GenericArrayType) type;
				Type componentType = arrayType.getGenericComponentType();
				if (TypeVariable.class.isAssignableFrom(componentType.getClass())) {
					String name = ((TypeVariable) componentType).getName();
					entityType = ReflectUtils.getActualGenericType(clz, (Class) ((TypeVariable)componentType).getGenericDeclaration(), name);
					return;
				} else {
					throw new RuntimeException("未支持的处理");
				}
			} else if (ParameterizedType.class.isAssignableFrom(type.getClass())) {
				// 参数化泛型的处理
				ParameterizedType paraType = (ParameterizedType) type;
				Type actualType = paraType.getActualTypeArguments()[0];
				if (actualType instanceof Class<?>) {
					entityType = (Class<?>) actualType;
					return;
				} else {
					String name = ((TypeVariable) actualType).getName();
					entityType = ReflectUtils.getActualGenericType(clz, (Class) ((TypeVariable)actualType).getGenericDeclaration(), name);
					return;
				}
			} else if (TypeVariable.class.isAssignableFrom(type.getClass())){
				TypeVariable actualType = (TypeVariable) type;
				String name = ((TypeVariable) actualType).getName();
				entityType = ReflectUtils.getActualGenericType(clz, (Class) ((TypeVariable)actualType).getGenericDeclaration(), name);
				return;
			}
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public Object execute(Object[] parameters) {
		List<Object> identities = ActionUtils.getAllObjectsAllowDuplicate(parameters);
		// 数组返回类型
		if (returnType.isArray()) {
			Object[] result = (Object[]) Array.newInstance(entityType, identities.size());
			for (int i = 0; i < identities.size(); i++) {
				result[i] = retrieveEntity(identities.get(i));
			}
			return result;
		}
		// 集合返回类型
		if (Set.class.isAssignableFrom(returnType)) {
			Set result = new HashSet(parameters.length);
			for (Object para : identities) {
				result.add(retrieveEntity(para));
			}
			return result;
		}
		// 列表返回类型
		if (List.class.isAssignableFrom(returnType)) {
			List result = new ArrayList(identities.size());
			for (Object para : identities) {
				result.add(retrieveEntity(para));
			}
			return result;
		}
		// 单一对象返回类型
		return retrieveEntity(parameters[0]);
	}

	/**
	 * 寻回实体
	 * @param identity 实体标识
	 * @return
	 */
	private Object retrieveEntity(Object identity) {
		if (!(identity instanceof Serializable))
			throw new IllegalArgumentException("实体主键必须是 Serializable 类型");
		if (annotation.lazy())
			return getJpaTemplate().getReference(entityType, (Serializable) identity);
		else
			return getJpaTemplate().find(entityType, (Serializable) identity);
	}
}
